#include "symboles.h"

#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

#include "win32define.h"
#include "symboles.h"

static int G_CurrentOffset = 0 ;

const int TYPE_SIZES[] = {
    [VOID] = 0,
    [INT] = 1,
    [CHAR] = 1
} ;

static struct Symbol *G_TopSymbol = NULL ;

/* Crée un symbole correspondant à la fonction "name" et le retourne. */
struct Symbol *createFunctionSymbol (char const * const name, struct SType type, struct SType *args) {
    struct Symbol *aux = malloc(sizeof(struct Symbol)) ;
    if (!aux) return NULL ;
    if (name) {
	aux->sName = strdup(name) ; 
	if (!aux->sName) {
	    free(aux) ;
	    return NULL ;
	}
    }
    else {
	aux->sName = NULL ;
    }
    aux->sBlockLevel = 0 ; 
    aux->sAddr = -1 ;
    aux->sInit = 0 ;
    aux->sUsed = 0 ;
    aux->sType = malloc(sizeof(struct SType)) ;
    *(aux->sType) = type ;
    aux->sType->ssConst = 1 ;
    aux->sType->ssNextArg = args ;
    aux->sNext = NULL ;
    return aux ;
}

/* Crée un symbole correspondant à la variable "name" et le retourne. */
struct Symbol *createVariableSymbol (char const * const name, int blockLevel, struct SType type) {
    struct Symbol *aux = malloc(sizeof(struct Symbol)) ;
    if (!aux) return NULL ;
    if (name) {
	aux->sName = strdup(name) ;
	if (!aux->sName) {
	    free(aux) ;
	    return NULL ;
	}
    }
    else {
	aux->sName = NULL ;
    }
    aux->sBlockLevel = blockLevel ; 
    aux->sAddr = 0 ;
    aux->sInit = 0 ;
    aux->sUsed = 0 ;
    aux->sType = malloc(sizeof(struct SType)) ;
    *aux->sType = type ;
    aux->sNext = NULL ;
    return aux ;
}

/* Libère le symbole pointé par sym. 
   ATTENTION : Ne doit pas être utilisé sur des symboles retournés par getSymbole. */
void freeSymbol (struct Symbol *sym) {
    struct SType *aux ;
    free(sym->sName) ;
    while (sym->sType) {
	aux = sym->sType ;
	sym->sType = aux->ssNextArg ;
	free(aux) ;
    }
    free(sym) ;
}

#define RETURN_ADDR_SIZE 1

/* Ajoute un symbole à la table si celui si n'existe pas, retourne 0 si tout s'est bien passé. */
int addSymbol (struct Symbol *sym) {
    if (isSymbolAtLevel(sym->sName, sym->sBlockLevel)) {
	return SYM_REDEFINED;
    }
    /* Si le symbole n'est pas de niveau 0 (global) ou 1 (paramètres), on incrémente l'offset. */
    if (sym->sBlockLevel > 1) {
	G_CurrentOffset += TYPE_SIZES[sym->sType->ssType] ;
	sym->sAddr = G_CurrentOffset ;
    }
    else if (sym->sBlockLevel > 0) {
        struct Symbol *aux ;
	/* Le symbole "en haut" de la pile d'arg à un offset de 1 par rapport à ebp. */
        sym->sAddr = RETURN_ADDR_SIZE + TYPE_SIZES[sym->sType->ssType] ; 
        aux = G_TopSymbol ;
        while (aux && aux->sBlockLevel > 0) { 
	    aux->sAddr += TYPE_SIZES[sym->sType->ssType] ;
	    aux = aux->sNext ;
	}
    }
    sym->sNext = G_TopSymbol ;
    G_TopSymbol = sym ;
    return 0 ;
}

/* Retourne le symbole correspond à name au niveau blockLevel, ou NULL si il n'existe pas. */
struct Symbol *getSymbolAtLevel (char const * const name, int blockLevel) {
    struct Symbol *addr = G_TopSymbol ;
    for (addr = G_TopSymbol ; addr && (strcmp(addr->sName, name) || addr->sBlockLevel != blockLevel) ; addr = addr->sNext) {

    }
    if (addr) addr->sUsed = 1 ;
    return addr ;
}

/* Retourne le symbole correspond à name au niveau ou en dessous de blockLevel, ou NULL si il n'existe pas. */
struct Symbol *getSymbolUnderLevel (char const * const name, int blockLevel) {
    struct Symbol *addr = G_TopSymbol ;
    for (addr = G_TopSymbol ; addr && (strcmp(addr->sName, name) || addr->sBlockLevel > blockLevel) ; addr = addr->sNext) {

    }
    if (addr) addr->sUsed = 1 ;
    return addr ;
}

/* Retourne le symbole correspond à la fonction nommée "name", ou NULL si il n'existe pas. */
struct Symbol *getFunctionSymbol (char const * const name) {
    struct Symbol *addr = G_TopSymbol ;
    for (addr = G_TopSymbol ; addr && (strcmp(addr->sName, name) || addr->sAddr >= 0) ; addr = addr->sNext) {

    }
    if (addr) addr->sUsed = 1 ;
    return addr ;
}

/* Retourne 1 si le symbole correspond à name existe au niveau blockLevel, 0 sinon. */
int isSymbolAtLevel (char const * const name, int blockLevel) {
    return getSymbolAtLevel(name, blockLevel) != NULL;
}

/* Retourne 1 si le symbole correspond à name existe au niveau ou en dessous de blockLevel, 0 sinon. */
int isSymbolUnderLevel (char const * const name, int blockLevel) {
    return getSymbolUnderLevel(name, blockLevel) != NULL;
}

/* Retourne la taille actuelle (en octets) de l'ensemble des symbols. */
int getTotalSymbolSize (void) {
    return G_CurrentOffset ;
}

/* Supprime tous les symbols de niveau level si level est le plus haut niveau de la liste,
   ne fait rien sinon.
   Retourne la taille (en octets) de symbols supprimés. 
*/
int removeMaxLevelSymbols (int level, struct Symbol **unused) {
    struct Symbol *aux ;
    int dataSize = 0 ;
    if (G_TopSymbol == 0) {
	return 0 ;
    }
    while (G_TopSymbol && G_TopSymbol->sBlockLevel == level) { 
	aux = G_TopSymbol ;
	G_TopSymbol = G_TopSymbol->sNext ;
	G_CurrentOffset -= TYPE_SIZES[aux->sType->ssType] ;
	dataSize += TYPE_SIZES[aux->sType->ssType] ;
        if (unused && !aux->sUsed) {
	    aux->sNext = *unused ;
	    *unused = aux ;
	}
	else {
	    freeSymbol(aux) ;
	}
    }
    return dataSize ;
}


/* Compare 2 types. */
int cmpType (const struct SType *t1, const struct SType *t2) {
    if (t1 == NULL && t2 == NULL) {
	return 1 ;
    }
    else if (t1 == NULL || t2 == NULL) {
	return 0 ;
    }
    else if (t1->ssType != t2->ssType) {
	return 0 ;
    }
    else {
	return cmpType(t1->ssNextArg, t2->ssNextArg) ;
    }
}
